/**
 * 
 */
package profile.aspect;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.lang.Thread.State;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * @author ductrung.tran
 *
 */
public class MethodCallRepository {

	private LinkedBlockingQueue<MethodCall> resultQueue = new LinkedBlockingQueue<MethodCall>();
	private String fileName;
	
	private long contextInactiveTimeout = Long.parseLong(System.getProperty("context.inactive.timeout", "3000"));
	
	private class WorkerThread extends Thread {
		@Override
		public void run() {
			System.out.println("ResultHandlerThread start");
			PrintWriter output = null;
			
			try {
				File file = new File(fileName);
				if (!file.getParentFile().exists()) {
					System.out.println("Directory " + file.getParent() + " not exist, try to create it");
					file.getParentFile().mkdirs();
				}
				output = new PrintWriter(new FileOutputStream(file));
				
				MethodCall ctx = null;
				
				int flushCount = 0;
				while((ctx = resultQueue.poll(contextInactiveTimeout, TimeUnit.MILLISECONDS)) != null 
						|| hasWatingProfile()) {
					
					if (ctx != null) {
						output.println(ctx);
						if (++flushCount == 10) {
							output.flush();
							flushCount = 0;
						}
						//output.flush();
					}
				}
				
				System.out.println("ResultHandlerThread exit after " + contextInactiveTimeout + " ms");
			} catch (Throwable e) {
				System.out.println("Warning : ResultHandlerThread is exit on error " + e);
			} finally {
				if (output != null) {
					try {
						output.close();
					} catch (Exception e) {e.printStackTrace();}
				}
			}
		}
	}
	
	private WorkerThread workerThread;
	private AtomicBoolean workerThreadStarted = new AtomicBoolean();
	
	private static MethodCallRepository INSTANCE;
	
	public static MethodCallRepository getInstance() {
		return INSTANCE;
	}
	
	public static void setInstance(MethodCallRepository handler) {
		INSTANCE = handler;
	}
	
	public MethodCallRepository(String fileName) {
		this.fileName = fileName;
		this.workerThread = new WorkerThread();
	}
	
	public void addProfilingResult(MethodCall ctx) {
		resultQueue.add(ctx);
		startWorkerThread();
	}

	private void startWorkerThread() {
		if (workerThreadStarted.compareAndSet(false, true)) {
			workerThread.start();
		}
	}

	public boolean hasWatingProfile() {
		System.out.println(contextInactiveTimeout);
		return !MethodCall.noActiveContextSince(contextInactiveTimeout);
	}
	
	public boolean isWorkerThreadTerminated() {
		return workerThread.getState() == State.TERMINATED;
	}

}
